package dai.engine

import dai.core.base.*
import dai.core.base.camera.QuakeCamera
import dai.core.base.light.PointLight
import dai.core.base.loader.ObjLoader
import org.joml.Matrix4f
import org.joml.Vector3f
import org.lwjgl.glfw.GLFW
import org.lwjgl.glfw.GLFWCursorEnterCallback
import org.lwjgl.glfw.GLFWCursorPosCallback
import org.lwjgl.opengl.GL11C
import org.lwjgl.opengl.GL30C
import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryStack.stackPush

class LightRender2 {

    val shaderProgram = ShaderProgram()
    val transformation = Transformation()
    val FOV = Math.toRadians(60.0).toFloat()
    val NEAR = 0.01f
    val FAR = 1000f
    var widthf = 0f
    var heightf = 0f
    val tmpMatrix = Matrix4f()

    val ambientLight = Vector3f(0.2f, 0.2f, 0.2f)
    val pointLight = PointLight()

    fun init(window: Window) {

        widthf = window.width.toFloat()
        heightf = window.height.toFloat()

        // init shader
        val vertexSource = Utils.loadShader("/light1.vs.shader")
        val fragSource = Utils.loadShader("/light1.fs.shader")
        shaderProgram.initShader(vertexSource, fragSource)

        // vs uniform
        shaderProgram.createUniform("projectMatrix")
        shaderProgram.createUniform("modelViewMatrix")

        // fs uniform
        shaderProgram.createUniform("texture_sampler")
        shaderProgram.createUniform("ambientLight")
        shaderProgram.createUniform("specularPower")

        // material
        shaderProgram.createUniform("material.ambient")
        shaderProgram.createUniform("material.diffuse")
        shaderProgram.createUniform("material.specular")
        shaderProgram.createUniform("material.hasTexture")
        shaderProgram.createUniform("material.reflectance")

        // point light
        shaderProgram.createUniform("pointLight.color")
        shaderProgram.createUniform("pointLight.position")
        shaderProgram.createUniform("pointLight.intensity")
        shaderProgram.createUniform("pointLight.att.constant")
        shaderProgram.createUniform("pointLight.att.linear")
        shaderProgram.createUniform("pointLight.att.exponent")

        GL30C.glClearColor(0f, 0f, 0f, 1f)
        GL30C.glEnable(GL30C.GL_DEPTH_TEST)
    }

    fun render(items: List<GameItem>, camera: QuakeCamera) {

        shaderProgram.bind()
        val projectMatrix = transformation.projectMatrix(FOV, widthf, heightf, NEAR, FAR)
        shaderProgram.setUniform("projectMatrix", projectMatrix)
        shaderProgram.setUniform("ambientLight", ambientLight)
        val viewMatrix = transformation.viewMatrix(camera)

        // render items
        for (item in items) {
            val worldMatrix = transformation.worldMatrix(
                item.position,
                item.rotation,
                item.scale
            )
            tmpMatrix.set(viewMatrix).mul(worldMatrix)
            shaderProgram.setUniform("modelViewMatrix", tmpMatrix)

            // material
            shaderProgram.setUniform("material.ambient", item.mesh.material.ambientColor)
            shaderProgram.setUniform("material.diffuse", item.mesh.material.diffuseColor)
            shaderProgram.setUniform("material.specular", item.mesh.material.specularColor)
            shaderProgram.setUniform("material.hasTexture", 0)
            shaderProgram.setUniform("material.reflectance", item.mesh.material.reflectance)

            // point light
            shaderProgram.setUniform("pointLight.color", pointLight.color)
            shaderProgram.setUniform("pointLight.position", pointLight.position)
            shaderProgram.setUniform("pointLight.intensity", pointLight.intensity)
            shaderProgram.setUniform("pointLight.att.constant", pointLight.constant)
            shaderProgram.setUniform("pointLight.att.linear", pointLight.linear)
            shaderProgram.setUniform("pointLight.att.exponent", pointLight.exponent)

            item.mesh.render {
                shaderProgram.setUniform("texture_sampler", 0)
                shaderProgram.setUniform("specularPower", item.mesh.material.specularPower)
            }
        }
        shaderProgram.unbind()
    }

    fun cleanup() {
        shaderProgram.cleanUp()
    }
}

class TestQuakeCamera : IGameLogic {

    val renderer = LightRender2()
    val camera = QuakeCamera(Vector3f(0f, 2f, 3f))
    var cameraInc = Vector3f()
    val items = ArrayList<GameItem>()

    var cursorPosCb: GLFWCursorPosCallback? = null

    //    var cursorEnterCb: GLFWCursorEnterCallback? = null
    var mouseValid = false
    val centerX = 400f
    val centerY = 300f

    override fun init(window: Window) {
        renderer.init(window)
        val cube = ObjLoader.loadMesh("/models/bunny.obj")
        cube.material = Material(
            //    texture = TextureCache.getTexture("textures/ball.png")
        )
        val cubeItem = GameItem(cube)
        cubeItem.position.z = -2f
        items.add(cubeItem)

        // 注册鼠标事件
        cursorPosCb = GLFW.glfwSetCursorPosCallback(window.winHandler) { winHandler, xpos, ypos ->
            val x = Utils.clampFloat(xpos.toFloat(), -400f, 400f)
            val y = Utils.clampFloat(ypos.toFloat(), -300f, 300f)
            camera.mouseX = x - centerX
            camera.mouseY = y - centerY
        }
//        cursorEnterCb = GLFW.glfwSetCursorEnterCallback(window.winHandler){winHandle,entered->
//            mouseValid = entered
//        }

        GLFW.glfwSetMouseButtonCallback(window.winHandler) { winHandle, button, action, mods ->
            if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT && action == GLFW.GLFW_PRESS) {
                camera.mouseDown = true
                MyLog.info("mouseDown is true")
            } else if (camera.mouseDown && button == GLFW.GLFW_MOUSE_BUTTON_LEFT && action == GLFW.GLFW_RELEASE) {
                camera.mouseDown = false
                MyLog.info("mouseDown is false")
            }
        }

//        stackPush().use {stack->
//            val width = stack.mallocInt(1)
//            val height = stack.mallocInt(1)
//            val winX = stack.mallocInt(1)
//            val winY = stack.mallocInt(1)
//
//            GLFW.glfwGetWindowSize(window.winHandler,width,height)
//            GLFW.glfwGetWindowPos(window.winHandler,winX,winY)
//            MyLog.info("width:${width[0]},height:${height[0]}")
//            MyLog.info("winX:${winX[0]},winY:${winY[0]}")
//            centerX = winX[0] + width[0]/2f
//            centerY = winY[0] + height[0]/2f
//            MyLog.info("centerX:$centerX ,centerY:$centerY")
//        }
    }

    override fun input(window: Window) {
        cameraInc.set(0f, 0f, 0f)
        camera.input(window = window)
    }

    override fun update(dt: Float) {
        camera.update(dt)
    }

    override fun render(window: Window) {
        if (window.bResize) {
            GL11C.glViewport(0, 0, window.width, window.height)
            window.bResize = false
        }
        GL11C.glClear(GL11C.GL_COLOR_BUFFER_BIT or GL11C.GL_DEPTH_BUFFER_BIT)
        renderer.render(items, camera)
    }

    override fun cleanup() {
        renderer.cleanup()
        cursorPosCb?.free()
//        cursorEnterCb?.free()
    }
}

fun main() {
    val window = Window(800, 600, "test quakeCamera")
    val engine = Engine(window, TestQuakeCamera())
    engine.start()
}